package org.btik.light.mobile.ui.serial.controller;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import org.btik.context.SimpleContext;
import org.btik.light.mobile.R;
import org.btik.light.mobile.databinding.FragmentSerialControllerMainBinding;
import org.btik.light.mobile.ui.factory.DevPanelFactory;
import org.btik.light.mobile.ui.serial.controller.entity.SerialPortInfo;
import org.btik.light.mobile.ui.serial.controller.meta.SerialDevMetaFactory;
import org.jetbrains.annotations.NotNull;


import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static org.btik.light.mobile.platform.conf.ContextGlobalNames.USB_ATTACH_HOOK;
import static org.btik.light.mobile.platform.conf.ContextGlobalNames.USB_PERMISSION_CALLBACK;

/**
 * @author lustre
 * @since 2023/4/3 17:57
 */
public class SerialControllerMainFragment extends Fragment implements USBAttachHook {
    private final HashMap<String, UsbSerialPort> usbSerialPorts = new HashMap<>();

    private final List<SerialPortInfo> serialPortInfos = new ArrayList<>();

    private final byte[] refresh = new byte[0];

    private FragmentSerialControllerMainBinding binding;

    private SerialPortAdapter serialPortAdapter;


    public static final String ACTION_USB_PERMISSION = "org.btik.light-handler.USB_PERMISSION";

    @Nullable
    @Override
    public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FragmentSerialControllerMainBinding.inflate(inflater, container, false);
        ListView portList = binding.portList;
        Spinner baud = binding.baud;
        // 115200
        baud.setSelection(12);
        baud.setOnItemSelectedListener(new SpinnerCheckListener(R.array.baudArray, baud.getContext()));
        binding.controllerType.setOnItemSelectedListener(new SpinnerCheckListener(R.array.controller_type, binding.controllerType.getContext()));
        listPort();
        serialPortAdapter = new SerialPortAdapter(portList.getContext(), SerialPortAdapter.getItemId(),
                serialPortInfos);
        portList.setAdapter(serialPortAdapter);

        SimpleContext.set(USB_ATTACH_HOOK, this);
        portList.setOnItemClickListener(new OpenSerialControllerPanel());
        UsbPermissionReceiver.reg(getActivity());
        return binding.getRoot();
    }

    @Override
    public void onDestroyView() {
        synchronized (refresh) {
            super.onDestroyView();
        }
        SimpleContext.remove(USB_ATTACH_HOOK);
    }

    private void listPort() {

        synchronized (refresh) {
            usbSerialPorts.clear();
            serialPortInfos.clear();
            UsbManager manager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
            List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
            if (availableDrivers.isEmpty()) {
                return;
            }

            for (UsbSerialDriver availableDriver : availableDrivers) {
                List<UsbSerialPort> ports = availableDriver.getPorts();
                for (UsbSerialPort usbSerialPort : ports) {
                    UsbDevice device = usbSerialPort.getDevice();
                    String deviceName = device.getDeviceName();
                    SerialPortInfo serialPortInfo = new SerialPortInfo(deviceName,
                            device.getProductName(), device.getManufacturerName());
                    int vendorId = device.getVendorId();
                    serialPortInfo.setVendorName(SerialDevMetaFactory.getVendorName(vendorId));
                    serialPortInfo.setVendorType(SerialDevMetaFactory.getVendorType(vendorId, device.getProductId()));
                    serialPortInfos.add(serialPortInfo);
                    // 如果有多个串口可能会出问题
                    usbSerialPorts.put(deviceName, usbSerialPort);
                }
            }
        }


    }

    @Override
    public void refresh() {
        FragmentActivity activity = getActivity();
        if (activity == null) {
            return;
        }
        activity.runOnUiThread(() -> {
            synchronized (refresh) {
                if (!isVisible()) {
                    return;
                }
                Context context = getContext();
                if (context == null) {
                    return;
                }
                if (binding == null) {
                    return;
                }
                SerialPortAdapter adapter = (SerialPortAdapter) binding.portList.getAdapter();
                adapter.clear();
                listPort();

            }
        });


    }

    static class SpinnerCheckListener implements Spinner.OnItemSelectedListener {
        private final String[] array;

        private String checked;

        public SpinnerCheckListener(int arrayId, Context context) {
            this.array = context.getResources().getStringArray(arrayId);
        }

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (array == null || array.length <= position) {
                return;
            }
            checked = array[position];
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    }

    class OpenSerialControllerPanel implements AdapterView.OnItemClickListener {

        UsbSerialPort usbSerialPort;
        int baudValue;
        Intent intent;
        FragmentActivity activity;

        UsbManager manager;

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            activity = getActivity();
            if (activity == null) {
                return;
            }

            manager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);
            SerialPortInfo serialPortInfo = serialPortAdapter.getItem(position);
            if (serialPortInfo == null) {
                return;
            }
            // 串口
            String devName = serialPortInfo.getDevName();
            usbSerialPort = usbSerialPorts.get(devName);
            if (usbSerialPort == null) {
                return;
            }
            // 波特率
            String baudValue = ((SpinnerCheckListener) binding.baud.getOnItemSelectedListener()).checked;
            this.baudValue = Integer.parseInt(baudValue);

            // 目标遥控器界面
            String controllerType = ((SpinnerCheckListener) binding.controllerType.getOnItemSelectedListener()).checked;
            String serialControllerPanelName = DevPanelFactory.getSerialControllerPanelNameByType(controllerType);

            intent = new Intent(serialControllerPanelName);
            SimpleContext.set(USB_PERMISSION_CALLBACK, usbSerialPort);

            if (checkPermission()) {
                openDevPanel();
            } else {
                openWithRequestPermission();
            }


        }

        private void openWithRequestPermission() {
            PendingIntent pendingIntent;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
                pendingIntent = PendingIntent.getBroadcast(activity, 0,
                        new Intent(ACTION_USB_PERMISSION), FLAG_IMMUTABLE);
            } else {
                pendingIntent = PendingIntent.getBroadcast(activity, 0,
                        new Intent(ACTION_USB_PERMISSION), 0);
            }

            SimpleContext.set(USB_PERMISSION_CALLBACK, (Runnable)() -> {
                if (checkPermission()) {
                    openDevPanel();
                }
            });
            manager.requestPermission(usbSerialPort.getDevice(), pendingIntent);
        }


        private void openDevPanel() {

            if (!openConnection()) {
                return;
            }

            if (Looper.myLooper() == Looper.getMainLooper()) {
                activity.startActivity(intent);
            } else {
                activity.runOnUiThread(() -> activity.startActivity(intent));
            }

            usbSerialPort = null;
            intent = null;

        }

        private boolean checkPermission() {
            if (usbSerialPort.isOpen()) {
                Toast.makeText(activity, R.string.serial_open_success, Toast.LENGTH_SHORT).show();
                return true;
            }
            UsbDevice device = usbSerialPort.getDevice();
            return manager.hasPermission(device);
        }

        private boolean openConnection() {
            if (usbSerialPort.isOpen()) {
                Toast.makeText(activity, R.string.serial_open_success, Toast.LENGTH_SHORT).show();
                return true;
            }
            UsbManager manager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);
            UsbDeviceConnection connection = manager.openDevice(usbSerialPort.getDevice());
            if (connection == null) {
                Toast.makeText(activity, R.string.serial_open_failed, Toast.LENGTH_SHORT).show();
                return false;
            }

            try {
                usbSerialPort.open(connection);
                usbSerialPort.setParameters(baudValue, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
                Toast.makeText(activity, R.string.serial_open_success, Toast.LENGTH_SHORT).show();
                return true;
            } catch (IOException e) {
                Toast.makeText(activity, getText(R.string.serial_open_failed) + ":" + e.getMessage(), +Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
            return false;


        }


    }


}


